package cn.xiaolang.function.retrying;

import java.util.Collections;

import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.backoff.Sleeper;
import org.springframework.retry.backoff.ThreadWaitSleeper;
import org.springframework.retry.policy.AlwaysRetryPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.policy.TimeoutRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

/**
 * RetryTemplate的execute 是线程安全的，实现逻辑使用ThreadLocal保存每个执行实例的RetryContext执行上下文。
 * 
 * 一个是 重试实体限定为Throwable子类，说明重试针对的是可捕捉的功能异常为设计前提的，
 * 但是我们希望依赖某个数据对象实体作为重试实体，但Sping-retry框架必须强制转换为Throwable子类。
 * 另一个就是重试根源的断言对象使用的是doWithRetry的Exception 异常实例，不符合正常内部断言的返回设计。
 * 
 * Spring Retry提倡以注解的方式对方法进行重试，重试逻辑是同步执行的，重试的“失败”针对的是Throwable，
 * 如果你要以返回值的某个状态来判定是否需要重试，可能只能通过自己判断返回值然后显式抛出异常了。
 * 
 * @author DeanKano
 * @date 2018年3月15日
 */
public class SpringRetryDemo {

	public static void main(String[] args) throws InterruptedException {
		retryHandler(-10L);
	}

	private static void retryHandler(Long id) throws InterruptedException {


		RetryTemplate retryler = new RetryTemplate();
		retryler.setRetryPolicy(new SimpleRetryPolicy(3,
				Collections.<Class<? extends Throwable>, Boolean>singletonMap(Exception.class, true)));

		FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
		backOffPolicy.setBackOffPeriod(1000);
		/**
		 * ExponentialBackOffPolicy：指数退避策略，需设置参数sleeper、initialInterval、maxInterval和multiplier，
		 * initialInterval指定初始休眠时间，默认100毫秒，
		 * maxInterval指定最大休眠时间，默认30秒，
		 * multiplier指定乘数，即下一次休眠时间为当前休眠时间*multiplier
		 */
		ThreadWaitSleeper threadWaitSleeper = new ThreadWaitSleeper();
		threadWaitSleeper.sleep(1000L);
		ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
		exponentialBackOffPolicy.setSleeper(threadWaitSleeper);
		exponentialBackOffPolicy.setMultiplier(3);
		exponentialBackOffPolicy.setMaxInterval(30);
		exponentialBackOffPolicy.setInitialInterval(1000);
		retryler.setBackOffPolicy(exponentialBackOffPolicy);


		RetryCallback<Boolean, Exception> retryCallback = new RetryCallback<Boolean, Exception>() {
			@Override
			public Boolean doWithRetry(RetryContext context) throws Exception {
				Exception ex = getName(id);
				if(ex != null) {
					throw ex;// 这个点特别注意，重试的根源通过Exception返回return
				}else {
					return false;
				}
			}
		};
		RecoveryCallback<Boolean> recoveryCallback = new RecoveryCallback<Boolean>() {
			@Override
			public Boolean recover(RetryContext context) throws Exception {
				System.err.println("重试结束");
				return false;
			}
		};
		try {
			retryler.execute(retryCallback, recoveryCallback);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static Exception getName(long id) {
		if (id < 0) {
			return new Exception("数据异常");// 这个点特别注意，重试的根源通过Exception返回return
		} else {
			System.err.println("xl:" + id);
			return null;
		}
	}
}
